Skip to content

feat!: XDG directory layout via VpDirs - #2346

Open
forehalo wants to merge 18 commits into
voidzero-dev:mainfrom
forehalo:feat/dirs-path-resolution
Open

feat!: XDG directory layout via VpDirs#2346
forehalo wants to merge 18 commits into
voidzero-dev:mainfrom
forehalo:feat/dirs-path-resolution

Conversation

@forehalo

@forehalo forehalo commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Close #827

Summary

Centralize on-disk paths in vp_shared::VpDirs and default fresh installs to the split XDG/platform layout. Existing default installs under ~/.vite-plus remain on the legacy monolithic root for now (grandfathered via existence-gated resolution). Automatic layout migration is out of scope here and tracked as follow-up work.

Resolution (high level)

Ordered sources (per category): deprecated VP_HOME (custom full-root pin) → existing ~/.vite-plus (exist-gated legacy mapping) → VP_BIN_DIR / VP_DATA_DIR / VP_CACHE_DIRXDG_* → platform defaults.

Category Split default (Unix) Legacy (~/.vite-plus)
bin ~/.local/bin <root>/bin
data ~/.local/share/vite-plus <root>
cache ~/.cache/vite-plus <root>/cache
config ~/.config/vite-plus <root>
state ~/.local/state/vite-plus <root>

Windows split uses %LOCALAPPDATA%\vite-plus\{bin,data,cache,state} and %APPDATA%\vite-plus for config. Relative VP_* / XDG_* values are treated as unset.

Commits (review order)

  1. docs(rfc): document split directory layout via VpDirs

    • Adds rfcs/directory-layout.md describing the resolution chain, grandfathering, installer alignment, and follow-ups (VP_HOME cleanup, migrate-on-upgrade).
    • Historical RFCs (e.g. env-command.md) are left unchanged.
  2. feat(shared): introduce VpDirs with strategy-gated path resolution

    • Replaces get_vp_home / home.rs with VpDirs + dirs/resolution.rs strategy chain.
    • Category roots only; first-level data subdirs (current, js_runtime, …) stay as joins on the owning feature.
    • Wires EnvConfig so tests isolate layout via with_vars / scoped without process-env leaks.
    • Updates AGENTS.md pointer to the central path API.
  3. refactor(cli): route on-disk paths through VpDirs

    • Migrates global CLI, shims, js_runtime, package manager, implode, env setup/doctor, and related helpers onto EnvConfig::get().dirs.
    • Env setup scripts re-export captured layout overrides (dir_envs); implode deletes deduped {data, cache, config, state} and never rm -rfs a shared bin dir.
    • Snapshot runner and fixtures still pin install roots explicitly (layout cleanup is chore: remove remaining VP_HOME usage (especially snapshot tests) #2371).
  4. feat(install): default installers to the split XDG layout

    • install.sh / install.ps1 / vp-setup / trampoline / install-global-cli install into the roots EnvConfig already resolved. Fresh install → split; existing ~/.vite-plus or --install-dir → monolithic root.
    • Directory env vars stay owned by EnvConfig. vp-setup does not read VP_HOME / VP_*_DIR / XDG_* itself. A local vp binary reports <DATA> / <BIN> / <CONFIG> via VP_DUMP_DIRS=1 so scripts and install-global-cli do not re-implement the chain.
    • Unix vp shims target <DATA>/current/bin/vp (not a version dir) after env setup --refresh. The Windows trampoline finds ../current or ../data/current from disk layout only.
    • Installer env docs and standalone-install CI: published-CLI jobs stay VP_HOME-pinned; a local-build job covers fresh split + grandfather.

User impact

Install Impact
Existing ~/.vite-plus Unchanged path (grandfathered until migrate follow-up)
Custom VP_HOME Still works (deprecated pin)
Fresh Split layout; typically only ~/.local/bin needs to be on PATH

Follow-up

Test plan

  • cargo check / unit tests for dirs resolution and layout helpers
  • Snapshot fixtures adapted for layout isolation (pin VP_HOME / disable shared seed where fakes are used)
  • Installer unification (single install.sh / install.ps1)
  • Formatting for RFC + install-global-cli
  • vp-setup / trampoline / Unix shim unit tests for fresh split vs grandfather
  • CI: test-standalone-install (released VP_HOME-pinned + local-build split/grandfather jobs)
  • Manual: fresh install lands on split paths; existing ~/.vite-plus still works without moving

@netlify

netlify Bot commented Aug 5, 2026

Copy link
Copy Markdown

Deploy Preview for viteplus-preview canceled.

Name Link
🔨 Latest commit bf8ca11
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/6a8332d71b7d6900087569ff

@forehalo
forehalo force-pushed the feat/dirs-path-resolution branch 4 times, most recently from b8069a5 to 2f8be03 Compare August 5, 2026 17:13
@forehalo forehalo changed the title feat: introduce Dirs for unified XDG-aware path resolution feat: XDG-compliant directory layout via unified Dirs resolution Aug 6, 2026
@forehalo
forehalo force-pushed the feat/dirs-path-resolution branch 2 times, most recently from 08b7589 to 4e2abdf Compare August 6, 2026 17:25
@forehalo forehalo changed the title feat: XDG-compliant directory layout via unified Dirs resolution feat: XDG directory layout via VpDirs Aug 6, 2026
@fengmk2

fengmk2 commented Aug 7, 2026

Copy link
Copy Markdown
Member

@liangmiQwQ Your refactor needs to wait until this merge is completed first, as the changes are expected to be quite significant.

@socket-security

socket-security Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedcargo/​temp-env@​0.3.68710093100100

View full report

@forehalo
forehalo force-pushed the feat/dirs-path-resolution branch 3 times, most recently from 6b26e69 to ca9dcca Compare August 7, 2026 12:17
@fengmk2 fengmk2 self-assigned this Aug 7, 2026
@fengmk2

fengmk2 commented Aug 7, 2026

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ca9dcca689

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/vp_trampoline/src/main.rs Outdated
Comment thread packages/cli/install.ps1 Outdated
Comment thread crates/vp_global_cli/src/commands/env/setup.rs Outdated
Comment thread packages/cli/src/config/hooks.ts Outdated
Comment thread crates/vp_shared/src/dirs/resolution.rs Outdated
Comment thread crates/vp_global_cli/src/commands/implode.rs Outdated
Comment thread crates/vp_shared/src/dirs/resolution.rs Outdated
@liangmiQwQ

liangmiQwQ commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

IMO, I would be a little bit curious about the benefit for this move. The current VP_HOME layout has been working well, the similar single-root approaches are also used by tools like Cargo, Bun, and Volta. Moving to a new layout is not a low-cost change: it affects existing installations, upgrade paths, user workflows, and debugging. For a released tool, I think we should carefully evaluate the trade-offs.

For example, I help maintain the Vite+'s global package installations in several, I'm concerned that using ~/.local/bin may introduce heavier binary conflicts and overlapping issues. I'm not sure if ~/.local/bin, a public bin location, should be managed (even partially) by Vite+.

I'm not against this direction, I just want to understand the motivation and the problem it solves. If it does help Vite+ improve, then now is really a good time to implement it (before RC).

Comment thread rfcs/directory-layout.md Outdated
@fengmk2

fengmk2 commented Aug 10, 2026

Copy link
Copy Markdown
Member

@liangmiQwQ Following the XDG directory convention will allow Vite+ to be more widely accepted, and many mature developer tools follow this convention, such as Claude Code and uv astral-sh/uv#8420

@forehalo
forehalo force-pushed the feat/dirs-path-resolution branch 2 times, most recently from ad9d7dd to 5cc3b6c Compare August 10, 2026 06:07
@fengmk2 fengmk2 added the preview-build Publish this PR's commits to the registry bridge as preview builds label Aug 10, 2026
@forehalo
forehalo marked this pull request as draft August 10, 2026 06:33
@fengmk2

fengmk2 commented Aug 16, 2026

Copy link
Copy Markdown
Member

@naokihaba If you have time, could you help take a look at whether this change has any impact on Nushell's functionality?

@naokihaba
naokihaba self-requested a review August 16, 2026 13:33
@naokihaba

Copy link
Copy Markdown
Collaborator

@fengmk2 Sure, I'll check it.

fengmk2

This comment was marked as resolved.

@fengmk2

fengmk2 commented Aug 17, 2026

Copy link
Copy Markdown
Member

I will take over this PR and fix the issues discovered during the testing process.

forehalo and others added 13 commits August 17, 2026 20:37
Capture the resolution design, grandfathering of existing ~/.vite-plus
installs, installer alignment, and follow-ups for VP_HOME cleanup and
migrate-on-upgrade.
Replace get_vp_home / the monolithic home module with VpDirs: category
roots (bin, data, cache, config, state) come from an ordered resolution
chain in dirs/resolution.rs (legacy Exist-gated grandfathering, VP_* and
XDG Set overrides, then platform defaults), and first-level data subdirs
(current, js_runtime, package_manager, packages, bins) are pure joins.
Files and deeper trees stay with their features.

Wire EnvConfig so test_guard/for_test_with_home isolate install roots
without process-env leaks. Groundwork for voidzero-dev#827.
Replace every get_vp_home()-era dependency with EnvConfig::get().dirs.
Business directory helpers stay in their owning modules but are
reimplemented on the category roots, so no call site constructs install
paths ad hoc.

- env setup scripts re-export the captured layout overrides
  (EnvConfig::env_exports) instead of hardcoding a VP_HOME line;
  vp-use.cmd does the same on Windows
- env doctor reports all five category roots; bin/config are hard
  errors when missing, data/cache/state are reported as lazily created
- implode deletes the deduped {data, cache, config, state} roots and
  removes only vp-owned files from the bin directory, which may be a
  shared ~/.local/bin
- upgrade-check cache moves to the state root; shim resolve cache
  lives under the cache root
Fresh installs write the split platform roots; an existing ~/.vite-plus
or --install-dir stays on the monolithic tree. Directory env vars stay
owned by EnvConfig: vp-setup reads only EnvConfig::get().dirs, and a
local vp binary reports those roots via VP_DUMP_DIRS so install.sh /
install.ps1 / install-global-cli do not re-implement the chain.

- Unix shims target <DATA>/current/bin/vp after env setup --refresh
- Windows trampoline finds ../current or ../data/current without
  reading VP_HOME / VP_*_DIR
- Standalone-install CI pins VP_HOME for published pre-split CLIs and
  adds a local-build job for fresh split + grandfather
CI still looks for $HOME/.vite-plus/bin/vp after bootstrap-cli:ci.
Create that directory before EnvConfig resolves so the existing-install
probe wins, without setting VP_HOME.

Also clone rolldown/vite before the local-build layout job so
`cargo build -p vp_global_cli` can load the workspace.
The test fetches the Node version index after scanning the cache. Without
an EnvConfig::with_vars_async pin it can observe a concurrent test's
VP_NODE_DIST_MIRROR=https://mirror.example/node and fail DNS lookup.
Implode only removes shims that resolve to this install's vp. Generated
env files persist resolved VP_*_DIR. Legacy Windows wrappers pin VP_HOME
to the data root. Upgrade-check cache moves to <CACHE>. Org-tarball cache
goes through EnvConfig.
Independent VP_BIN_DIR / VP_DATA_DIR installs cannot be found by
sibling-path probing. Write <name>.shim next to each trampoline and
resolve vp.exe from that pointer. Align install.ps1 with Windows
known folders so installer roots match EnvConfig.
install.sh now links <BIN>/vp to the absolute
<DATA>/current/bin/vp path. The grandfather layout job still
asserted the old relative ../current/bin/vp target.
Avoid the PowerShell 5 $HOME collision in install.ps1. Require a
matching <name>.shim before implode deletes Windows exes. Pin
VP_HOME from the trampoline for single-root bin/data layouts.
Write JetBrains node paths from getVpDirs(). Persist VP_HOME in
dir_envs only when it is an absolute path.
Pre-split binaries resolve every path from VP_HOME (default ~/.vite-plus),
so installing one into split roots produced a broken PATH vp next to a
half-built legacy tree while still exiting 0. install.sh, install.ps1, and
vp-setup now probe the downloaded payload with VP_DUMP_DIRS=1 before the
layout is final: a split-aware binary reports its category roots, which the
installer adopts verbatim, and a binary that cannot answer falls back to
the monolithic root (VP_HOME or ~/.vite-plus) with a notice. This also
keeps fresh installs of latest working until the first split-aware release
ships.

vp-setup returns the effective directories for its success summary and
removes the abandoned split data root that its managed node/pnpm downloads
land in when this run created it.

New test-install-sh-old-version (Linux, macOS) and
test-install-ps1-old-version (Windows) CI jobs install 0.2.9 with no
VP_HOME and assert the legacy layout and working PATH-resolved commands.
The mechanism is documented in rfcs/directory-layout.md and the VP_VERSION
entry of docs/guide/installer-env-vars.md.
with_vars_home_yields_split_layout pinned only HOME/USERPROFILE, so a
shell that exports VP_HOME (vp's own env script does) or XDG_* resolved
those overrides instead of the platform tail and failed. Clear every
layout override var in the test.
@fengmk2
fengmk2 force-pushed the feat/dirs-path-resolution branch from 960d13f to 9c48fa6 Compare August 17, 2026 12:41
A pre-split local vite-plus can create ~/.vite-plus at any time on a
machine whose global install is split (caches, config, managed runtimes).
Because the grandfather source outranks VP_*_DIR, that stray tree captured
the split install: vp upgrade and reinstalls silently moved to the
monolithic root while the split PATH entries kept serving the old binary.

Gate grandfathering on the current link every global install activates,
checked without following it so a dangling link from a crashed upgrade
still counts. install.sh and install.ps1 mirror the same gate; vp-setup
inherits it through EnvConfig.

The test-install-sh-layout CI job seeds a real install (version dir plus
current link) for the reuse step and gains a stray-tree step: split
install, stray ~/.vite-plus, then resolution and a reinstall must stay on
the split roots.
@fengmk2 fengmk2 added preview-build Publish this PR's commits to the registry bridge as preview builds and removed preview-build Publish this PR's commits to the registry bridge as preview builds labels Aug 17, 2026
…setup

Apply cleanup findings from a four-angle review of the take-over commits:

- Define the monolithic category mapping once per language:
  resolution::single_root_dirs backs VpDirs::legacy_single_root,
  set_monolithic_layout backs install.sh, New-MonolithicLayout backs
  install.ps1 (each replaced three hand-synced copies).
- vp-setup skips the probe when the resolved dirs already equal the
  monolithic root, and otherwise adopts the payload's reported
  data/bin/config verbatim like the scripts do, so the written layout
  cannot drift from the binary's own resolution.
- Share the VP_DUMP_DIRS category keys between printer and parser, and
  a LAYOUT_OVERRIDE_VARS list between the test env-clearing sites.
- install.ps1's grandfather gate checks the current link without
  following it (Get-Item -Force), matching the sh and Rust gates for
  dangling links; unblock only the probed binary instead of a second
  blanket pass.
- Hoist constant installer env to job level in the layout CI job and
  reference a job-level VP_VERSION in the pre-split jobs.
@fengmk2

fengmk2 commented Aug 17, 2026

Copy link
Copy Markdown
Member

@codex review

Apply an STE-style pass to the prose the take-over commits added. Split
long multi-clause sentences in the RFC, use active voice, and replace the
arrow chains with plain sentences. Use one term per concept: monolithic
root/layout in prose (code symbols keep legacy names), pre-split for the
release age.

Reword the fallback notice from "predates the split directory layout;
installing to <root>" to "does not support the split directory layout;
the install goes to <root>" in install.sh, install.ps1, and vp-setup,
with the CI greps and the RFC quote synced. Verified against a live 0.2.9
install.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 032b86f314

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +106 to +110
HashMap::from([
(env_vars::VP_BIN_DIR, dirs.bin.as_path().to_string_lossy().into_owned()),
(env_vars::VP_DATA_DIR, dirs.data.as_path().to_string_lossy().into_owned()),
(env_vars::VP_CACHE_DIR, dirs.cache.as_path().to_string_lossy().into_owned()),
])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve config and state roots in persisted environments

When vp env setup runs with a one-shot XDG_CONFIG_HOME or XDG_STATE_HOME and its generated environment is later sourced without that variable, this map pins only bin, data, and cache. The child then resolves config and state from platform defaults, so config.json, session state, and later setup output diverge from the installation that generated the environment. Fresh evidence beyond the earlier XDG comment is that the final implementation materializes only these three VP_* variables while claiming to reproduce identical roots; persist the effective config/state inputs as well.

AGENTS.md reference: AGENTS.md:L47-L47

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessed as correct but an edge: it triggers only when a one-shot XDG_CONFIG_HOME/XDG_STATE_HOME is set during vp env setup and absent later. Persisting config/state needs new VP_CONFIG_DIR/VP_STATE_DIR variables in the resolution chain (only bin/data/cache have override vars today), and the current XDG-as-session-policy behavior is documented as deliberate. Leaving this open as a design decision.

}

fn child_dir_pins(bin_dir: &std::path::Path, data: &std::path::Path) -> ChildDirPins {
if bin_dir == data.join("bin").as_path() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Record split layout instead of inferring it from path shape

When a supported split Windows install sets, for example, VP_DATA_DIR=D:\vp and VP_BIN_DIR=D:\vp\bin without setting VP_HOME, this equality misclassifies it as a single-root install. Every trampoline consequently injects VP_HOME=D:\vp, relocating cache, config, and state under the data root instead of using the split roots selected by VpDirs; the sidecar needs to record layout provenance or enough roots rather than deriving the mode from this valid directory relationship.

AGENTS.md reference: AGENTS.md:L47-L47

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessed as correct but narrow: it needs VP_DATA_DIR/VP_BIN_DIR values that exactly mimic the monolithic shape (bin == data\bin). The clean fix records layout provenance in the .shim sidecar, which is a format change to the sidecar contract. Leaving this open as a follow-up decision rather than folding it into the pre-split compatibility work.

Comment thread packages/tools/src/install-global-cli.ts Outdated
Comment thread crates/vp_global_cli/src/commands/upgrade/mod.rs
@fengmk2 fengmk2 changed the title feat: XDG directory layout via VpDirs feat!: XDG directory layout via VpDirs Aug 17, 2026
Grandfathering now requires the current link, so the bare ~/.vite-plus
directory the CI seeding created no longer selects the monolithic root.
The bootstrap then installed vp under ~/.local/bin while ci.yml and the
snapshot jobs look for ~/.vite-plus/bin, which failed eight jobs. Pin
VP_HOME for the CI bootstrap instead; the pin reaches the VP_DUMP_DIRS
probe and the install script through process.env.
On a split install, vp upgrade installed a pre-split payload into the
split data root and activated it; the old binary then moved config,
runtimes, and later upgrades to ~/.vite-plus while the split PATH entries
kept serving the old version.

Gate the target version before the download: below 0.3.0 is rejected
with a message that names the minimum version, preview builds
(0.0.0-commit.<sha>) are allowed, and monolithic installs (VP_HOME pin
or grandfathered ~/.vite-plus) accept every release, so the existing CI
upgrade tests keep their targets. The upgrade path uses a version gate,
not the payload probe: the running binary is split-aware by definition,
so the boundary is a fixed release number.

Move probe_payload_dirs into vp_setup with unit tests; vp-setup keeps
using it, the upgrade path does not.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview-build Publish this PR's commits to the registry bridge as preview builds test: create-e2e Run `vp create` e2e tests test: e2e Auto run e2e tests test: install-e2e run vite install e2e test test: sfw

Projects

None yet

Development

Successfully merging this pull request may close these issues.

use xdg config

4 participants